using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Text;
using System.Web;

using Awaken.Framework.Configuration;

namespace Awaken.Framework
{
    /// <summary>
    /// Represents a factory for creating Gateway objects.
    /// </summary>
    public sealed class GatewayFactory
    {
        private GatewayFactory() { }

        /// <summary>
        /// Creates a new Gateway object.
        /// </summary>
        /// <returns></returns>
        public static Gateway CreateGateway()
        {
            Gateway obj = new Gateway();
            foreach( Database db in GatewayFactory.Databases )
            {
                obj.Databases.Add(db);
            }
            Hashtable ht = GetTypeToDatabaseTable(obj.Databases);
            foreach( string key in ht.Keys )
            {
                obj.TypeToDatabaseTable.Add(key,ht[key]);
            }
            return obj;   
        }

        /// <summary>
        /// Returns a hashtable of types mapped to the databases 
        /// that contain them.  The type name is used as they hashtables
        /// key -- which is used for retrieving values.
        /// </summary>
        /// <param name="databases"></param>
        /// <returns></returns>
        internal static Hashtable GetTypeToDatabaseTable(Collection<Database> databases)
        {
            Hashtable table = new Hashtable();

            foreach (Database database in databases)
            {
                foreach ( System.Type type in database.MappedTypes) 
                {
                    if (database.Contains(type))
                    {
                        table.Add(type.ToString(), database);
                    }
                }
            }

            return table;
        }

        /// <summary>
        /// Returns the relative ("~/") path to the AwakenSetup.config
        /// file.  By default this value is set to "~/AwakenSetup.config".
        /// This value can be overridden using AppSettings["AwakenSetup.config"]. 
        /// </summary>
        /// <returns></returns>
        private static string RelativeConfig
        {
            get
            {
                string relativePath = "~/AwakenSetup.config";

                if (ConfigurationManager.AppSettings["AwakenSetup.config"] != null)
                {
                    relativePath = ConfigurationManager.AppSettings["AwakenSetup.config"].ToString();
                }
                return relativePath;
            }
        }

        /// <summary>
        /// Returns the physical path to the AwakenSetup.config
        /// file.
        /// </summary>
        /// <returns></returns>
        private static string PhysicalConfigPath
        {
            get
            {
                string relativePath = GatewayFactory.RelativeConfig;

                string physicalPath = string.Empty;

                if (File.Exists(relativePath))
                {
                    physicalPath = relativePath;
                }
                else
                {
                    if (HttpContext.Current == null)
                    {
                        physicalPath = System.Environment.CurrentDirectory + @"\" + relativePath.Replace(@"~/", "");
                    }
                    else
                    {
                        physicalPath = HttpContext.Current.Server.MapPath(relativePath);
                    }
                }

                return physicalPath;
            }
        }
        
        /// <summary>
        /// Returns the list of databases configured in the 
        /// AwakenSetup.config file.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// The nhibernate database configuration is setup using a custom
        /// xml config file -- AwakenSetup.config. 
        /// 
        /// The basic structure of this file is defined in \Configuration\AwakenSetup.xml.
        /// 
        /// The structure is then read into a strongly typed dataset using the AwakenSetup.cs
        /// class which is generated by Visual Studio.
        /// </remarks>
        private static Collection<Database> Databases
        {
            get
            {
                Collection<Database> databases = new Collection<Database>();

                AwakenSetup setup = new AwakenSetup();

                setup.ReadXml(PhysicalConfigPath);

                foreach (AwakenSetup.DatabaseRow row in setup.Database.Rows)
                {
                    Database db = new Database();

                    if (!row.IsNull("Name"))
                        db.Name = row.Name;

                    if (!row.IsNull("ConnectionDriver"))
                        db.Configuration.Properties.Add("hibernate.connection.driver_class", row.ConnectionDriver);

                    if (!row.IsNull("ConnectionProvider"))
                        db.Configuration.Properties.Add("hibernate.connection.provider", row.ConnectionProvider);

                    if (!row.IsNull("ConnectionString"))
                    {
                        db.ConnectionString = row.ConnectionString;
                        db.Configuration.Properties.Add("hibernate.connection.connection_string", row.ConnectionString);
                    }

                    if (!row.IsNull("Dialect"))
                        db.Configuration.Properties.Add("hibernate.dialect", row.Dialect);

                    if (!row.IsNull("PrepareSql"))
                        db.Configuration.Properties.Add("hibernate.prepare_sql", row.PrepareSql);

                    if (!row.IsNull("ShowSql"))
                        db.Configuration.Properties.Add("hibernate.show_sql", row.ShowSql);

                    if (!row.IsNull("UseOuterJoin"))
                        db.Configuration.Properties.Add("hibernate.use_outer_join", row.UseOuterJoin);

                    if (!row.IsNull("DefaultSchema"))
                        db.Configuration.Properties.Add("hibernate.default_schema", row.DefaultSchema);

                    if (!row.IsNull("ConnectionIsolation"))
                        db.Configuration.Properties.Add("hibernate.connection.isolation", row.ConnectionIsolation);

                    if (!row.IsNull("MappingAssemblies"))
                    {
                        string[] assemblies = row.MappingAssemblies.Split(char.Parse(","));
                        foreach (string assembly in assemblies)
                        {
                            db.Configuration.AddAssembly(assembly);
                        }
                    }

                    db.IsConfigured = true;

                    databases.Add(db);
                }
                return databases;
            }
        }
    }
}
